防抖(Debounce)与节流(Throttle)
一、核心原理对比
特性 | 防抖(Debounce) | 节流(Throttle) |
---|---|---|
核心思想 | "等你说完我再响应" | "按固定节奏响应" |
触发策略 | 延迟执行,期间新触发会重置计时 | 固定时间间隔内只执行一次 |
类比场景 | 电梯门(最后一个人进后关门) | 机枪扫射(按固定频率发射) |
典型应用 | 搜索联想、窗口 resize 结束监听 | 滚动加载、高频点击按钮 |
二、手写实现代码
1. 防抖函数(基础版 + 增强版)
js
function debounce(func, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
javascript
/**
* 防抖函数
* @param {Function} fn - 目标函数
* @param {number} delay - 延迟时间(ms)
* @param {boolean} immediate - 是否立即执行
* @returns {Function} 防抖处理后的函数
*/
function debounce(fn, delay = 300, immediate = false) {
let timer = null;
return function(...args) {
const context = this;
// 清除已有定时器
if (timer) clearTimeout(timer);
// 立即执行模式
if (immediate && !timer) {
fn.apply(context, args);
}
timer = setTimeout(() => {
timer = null;
if (!immediate) {
fn.apply(context, args);
}
}, delay);
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2. 节流函数(时间戳 + 定时器双模式)
js
function throttle(func, delay) {
let lastTime = 0;
return function(...args) {
const now = new Date().getTime();
if (now - lastTime >= delay) {
func.apply(this, args);
lastTime = now;
}
};
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
javascript
/**
* 节流函数
* @param {Function} fn - 目标函数
* @param {number} interval - 时间间隔(ms)
* @param {boolean} trailing - 是否在间隔末尾执行
* @returns {Function} 节流处理后的函数
*/
function throttle(fn, interval = 300, trailing = true) {
let lastTime = 0;
let timer = null;
return function(...args) {
const context = this;
const now = Date.now();
// 时间差计算
const remaining = interval - (now - lastTime);
if (remaining <= 0) {
// 到达间隔时间立即执行
if (timer) {
clearTimeout(timer);
timer = null;
}
fn.apply(context, args);
lastTime = now;
} else if (trailing && !timer) {
// 间隔末尾补发执行
timer = setTimeout(() => {
fn.apply(context, args);
lastTime = Date.now();
timer = null;
}, remaining);
}
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
三、使用示例
javascript
// 防抖应用:搜索框联想
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', debounce(function(e) {
console.log('搜索关键词:', e.target.value);
}, 500));
// 节流应用:滚动加载
window.addEventListener('scroll', throttle(function() {
console.log('当前滚动位置:', window.scrollY);
}, 1000));
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
四、实现关键点解析
功能 | 防抖实现要点 | 节流实现要点 |
---|---|---|
定时器管理 | 每次触发重置定时器 | 通过时间戳判断间隔,定时器用于补发执行 |
上下文绑定 | 使用 apply 保持 this 指向正确 | 同上 |
参数传递 | ...args 保留所有原始参数 | 同上 |
边缘情况处理 | 处理立即执行模式 | 处理首尾触发模式 |
五、高级特性扩展
取消功能
javascript// 防抖增加取消方法 function debounce(...) { const debounced = function(...args) { ... }; debounced.cancel = () => { clearTimeout(timer); timer = null; }; return debounced; }
1
2
3
4
5
6
7
8
9返回值处理
(注:异步函数无法直接返回结果,可通过回调或 Promise 处理)RAF 节流
javascript// 使用 requestAnimationFrame 实现动画节流 function throttleRAF(fn) { let ticking = false; return function(...args) { if (!ticking) { requestAnimationFrame(() => { fn.apply(this, args); ticking = false; }); ticking = true; } }; }
1
2
3
4
5
6
7
8
9
10
11
12
13
总结
- 防抖:高频事件结束后触发,避免重复执行(如输入验证)
- 节流:高频事件按节奏触发,保障最低执行频率(如滚动计算)
- 生产建议:直接使用 Lodash 的
_.debounce
和_.throttle
(已处理大量边界情况)